/// 分类器
module jinji.post.classify;

import jinji.common.query,
jinji.common.type;
import jinji.post.model,
jinji.rpc,
jinji.util.tag,
tame.buffer;

extern (C):

mixin getObject!(Classifier, "getClassifier", "查询分类器", "分类器", "name");

/// 添加/修改/删除分类器
@"删除数量"@type("[number]")
@Action void setClassifier(ref Request req, @"名称" string name, @"命令行" string cmd,
	@"描述" string desc = "", @"自动运行"bool autorun = true, @"使用的UID"long uid = 0) {
	req.modify!Classifier(
		parseName(name),
		parseText!"命令行"(cmd, 256, BitFlags!Allow(Allow.empty)),
		parseDesc(desc),
		autorun,
		uid ? parseId(uid) : req.uid,
		Id.empty,
		req.uid,
		now,
		now,
	);
}

/// 分类文件
@"标签串和错误信息"@type("[string, string]")
@Action void classifyFile(ref Request req, @"分类器" string name, @"文件" string file) {
	auto q = query!(SB.select!(Classifier.cmd)
			.where("name=$1"))(name);
	checkArg(name.stringof, !q.empty, "分类器不存在");
	StringSink err;
	req.send(classify(q.get!string, file, err));
	req.send(err[]);
}

/++
分类帖子
Params:
	pid = 帖子ID
+/
void classifyPost(Id pid) @trusted {
	import jinji.link.model,
	jinji.post.cr,
	jinji.util.string;

	auto q = query!(SB.select!(Post.hash, Post.tags)
			.where("id=$1"))(pid);
	checkArg(pid.stringof, !q.empty, "帖子不存在");
	const hash = q.get!(Hash, 0);
	const oldTags = q.get!(string, 1);
	foreach (row; query!(SB.select!(Link.url).where("hash=$1"))(hash[])) {
		auto url = row.get!string;
		if (!url.isFileURL)
			continue;

		url = url[7 .. $];
		if (url.length) {
			if (url[0] == '/')
				url = url[1 .. $];
			foreach (r; query!(SB.select!Classifier
					.where("autorun=1"))) {
				const c = r.get!Classifier;
				StringSink err;
				const tags = classify(c.cmd, url, err);
				const desc = err.length ? cast(string)err[] : "";
				requestChange(c.uid, pid, mergePatch(oldTags, tags),
					c.name, parseDesc(desc));
				if (!err.length) { // 更新分类器PID
					exec!(SB.update!Classifier
							.set("pid=$1").where("name=$2"))(pid, c.name);
				}
			}
		}
	}
}

/++
使用指定分类器对文件进行分类
Params:
	cmd = 分类器命令
	file = 文件路径
	err = 错误信息，若错误信息长度超过err的长度，则截断
Returns:
	分类结果
+/
extern (D) Tags classify(in char[] cmd, in char[] file, ref StringSink err) {
	import jinji.util.io,
	std.utf,
	tame.string;

	Cstr[2] args = [cmd, file];
	string s;
	foreach (line; ByTerminator!1024(spawn(args, stdin, &err), '\n')) {
		const i = line.indexOf('%');
		if (i > 0) {
			const tag = line[i + 2 .. $];
			s = applyPatch(s, cast(string)tag);
		}
	}
	validate(s);
	return cast(Tags)s;
}
